#include "twistingwidget.h"

TwistingWidget::TwistingWidget(QWidget *parent)
	: QGLWidget(parent)
{
	fTwistingParameterValue = 0.0;
	fTwistingDirection =0.2;
	fWireframed = false;
	fAnimated = true;
}

TwistingWidget::~TwistingWidget()
{
	cgDestroyProgram(fCgVertexProgram);
	cgDestroyProgram(fCgFragmentProgram);
	cgDestroyContext(fCgContext);
}

void TwistingWidget::initializeGL()
{
	glClearColor(1.0,0.5,0.2,1.0);
	initCG();
	connect(&fTimer,SIGNAL(timeout()),this,SLOT(animate()));
	fTimer.start(10);
}

void TwistingWidget::resizeGL(int w, int h )
{
	if(h==0)
		h=1;
	glViewport(0,0,w,h);
}

void TwistingWidget::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT);
	display();
}	

void TwistingWidget::display()
{
	cgSetParameter1f(fCgVertexParam_twisting,fTwistingParameterValue);
	cgGLBindProgram(fCgVertexProgram);
	cgGLEnableProfile(fCgVertexProfile);
	cgGLBindProgram(fCgFragmentProgram);
	cgGLEnableProfile(fCgFragmentProfile);
	makeModel();
}


void TwistingWidget::makeModel()
{
	drawSubDividedTriangle(6);
}

void TwistingWidget::initCG()
{
	fCgContext = cgCreateContext();
	cgGLSetDebugMode(GL_FALSE);
	cgSetParameterSettingMode(fCgContext,CG_DEFERRED_PARAMETER_SETTING);
	fCgVertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
	cgGLSetOptimalOptions(fCgVertexProfile);
	checkForCgError("creating context");
	fCgVertexProgram = cgCreateProgramFromFile(fCgContext,CG_SOURCE,"C4E1v_twisting.cg",fCgVertexProfile,"C4E1v_twisting",NULL);
	checkForCgError("creating vertex program");
	fCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(fCgFragmentProfile);
	fCgFragmentProgram = cgCreateProgramFromFile(fCgContext,CG_SOURCE,"C4E1f_twisting.cg",fCgFragmentProfile,"C4E1f_twisting",NULL);
	checkForCgError("creating fragment program");
	cgGLLoadProgram(fCgVertexProgram);
	cgGLLoadProgram(fCgFragmentProgram);
	checkForCgError("error");
	fCgVertexParam_twisting = cgGetNamedParameter(fCgVertexProgram,"twisting");
	cgSetParameter1f(fCgVertexParam_twisting,3.0);
}

void TwistingWidget::checkForCgError(const char *situation)
{
	CGerror error;
	const char * info = cgGetLastErrorString(&error);
	if(error != CG_NO_ERROR){
		QString msg = QString("Twisting Triangle:  %1 %2").arg(situation).arg(info);
		QMessageBox::information(NULL,"cg error",msg);
		exit(1);
	}
}


void TwistingWidget::triangleDivide(int depth , const float a[2] , const float b[2] , const float c[2] , const float ca[3],const float cb[3] , const float cc[3])
{
	if(depth ==0){
		glColor3fv(ca);
		glVertex2fv(a);
		glColor3fv(cb);
		glVertex2fv(b);
		glColor3fv(cc);
		glVertex2fv(c);
	}else{
		const float d[2] = {(a[0]+b[0])/2,(a[1]+b[1])/2}, 
			e[2] = {(b[0]+c[0])/2,(b[1]+c[1])/2},
			f[2] = {(c[0]+a[0])/2,(c[1]+a[1])/2};
		const float cd[3] = {(ca[0]+cb[0])/2,(ca[1]+cb[1])/2,(ca[2]+cb[2])/2},
			ce[3] = {(cb[0]+cc[0])/2,(cb[1]+cc[1])/2,(cb[2]+cc[2])/2},
			cf[3] = {(cc[0]+ca[0])/2,(cc[1]+ca[1])/2,(cc[2]+ca[2])/2};
		depth-=1;
		triangleDivide(depth,a,d,f,ca,cd,cf);
		triangleDivide(depth,d,b,e,cd,cb,ce);
		triangleDivide(depth,d,e,f,cd,ce,cf);
		triangleDivide(depth,f,e,c,cf,ce,cc);
	}
}

void TwistingWidget::drawSubDividedTriangle(int subdivisions)
{
	const float a[2] = {-0.8,0.8}, b[2] = {0.8,0.8},c[2] ={0.0,-0.8},
		ca[3] = {1,0,0},cb[3] = {0,1,0},cc[3] = {0,0,1};

	glBegin(GL_TRIANGLES);
		triangleDivide(subdivisions,a,b,c,ca,cb,cc);
	glEnd();
}

void TwistingWidget::animate()
{
	if(fTwistingParameterValue > 4.0){
		fTwistingDirection=-0.05;
	}else if(fTwistingParameterValue < -4.0){
		fTwistingDirection = 0.05;
	}
	fTwistingParameterValue+= fTwistingDirection;
	updateGL();
}


void TwistingWidget::keyPressEvent(QKeyEvent *ev)
{
	switch(ev->key()){
		case Qt::Key_0:
			fWireframed=!fWireframed;
			if(fWireframed){
				glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
			}else
				glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
			break;
		case Qt::Key_1:
			fAnimated= !fAnimated;
			if(fAnimated){
				fTimer.start();
			}else{
				fTimer.stop();
			}
			break;
	}
	updateGL();
}